﻿using Audio;
using Network;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using VoiceAI;
using Windows.Data.Json;
using Xiaowei.Diagnostics;
using Xiaowei.ResponseHandlers;
using Xiaowei.Settings;
using XiaoweiV2;
using Log = Xiaowei.Diagnostics.Debug;
namespace Xiaowei.Services
{
    public class XiaoweiAI
    {
        private class CustomVoiceAI : IVoiceAI
        {
            public event Action<IResponseData> ResponseReceived;
            public void SendCustomResponse(IResponseData responseData)
            {
                ResponseReceived?.Invoke(responseData);
            }
        }

        public enum XiaoweiAIStateEnum
        {
            Idle=1,
            ListeningPrepare=2,
            Listening=3,
            Thinking=4,
            Speaking=5,
            Mute=6
        }
        public event Action<XiaoweiAIStateEnum> XiaoweiAIStateChanged;
        private object stateChangeLocker = new object();

        private XiaoweiAIStateEnum xiaoweiAIState = XiaoweiAIStateEnum.Idle;

        public XiaoweiAIStateEnum XiaoweiAIState
        {
            get
            {
                return xiaoweiAIState;
            }
            private set
            {
                if (xiaoweiAIState != value)
                {
                    Debug.WriteLine($"XiaoweiAIState  {value}");
                    lock (stateChangeLocker)
                    {
                        Debug.WriteLine($"XiaoweiAIState  {value}  enter lock");
                        xiaoweiAIState = value;
                        if (value == XiaoweiAIStateEnum.Listening)
                        {
                            StartSpeechRecognize();
                        }
                        else
                        {
                            StopSpeechRecognize();
                        }
                        XiaoweiAIStateChanged?.Invoke(value);
                    }
                    Debug.WriteLine($"XiaoweiAIState  {value}  exit lock");

                }
            }
        }
        #region Use for keyword recognize.
        public event KeywordEventHandler KeywordConfirmed;
        public event KeywordEventHandler KeywordRejected;
        #endregion



        //this event can notified when xiaoweiSDK disconnected.
        public event AvailablityEventHandler OnlineStateChanged;

        internal void GetDeviceList()
        {
            _=onlineSDK.GetDeviceListAsync();
        }

        public event ResponseErrorEventHandler ResponseErrored;

        public event QrCodeEventHandler QrCodeChanged;

        //public static event AlarmEventHandler AlarmChanged;

        public event ConversationCountHandler CountChanged;

        //this event can notified when changed to onlineMode(true) or offlineMode(false).
        //You can just play tts or other notity to user when it occur.The service can auto abort active state.
        public event Action<bool> OnlineModeChanged;
        private bool isOnlineMode = true;
        public bool IsOnlineMode {
            get
            {
                return isOnlineMode;
            }
            private set
            {
                if(isOnlineMode!=value)
                {
                    _ = AbortRequestAsync();
                    isOnlineMode = value;
                    OnlineModeChanged?.Invoke(value);
                }
            }
        }

        internal void ReportState(Skill skill, string playId, string content, PlayMode mode, PlayState state, ulong offset)
        {
            onlineSDK.ReportState(skill, playId, content, mode, state, offset);
        }

        public bool IsOnline => onlineSDK.IsOnline;


        private bool isLogin=false;

        public bool IsLogin
        {
            get
            {
                return isLogin;
            }
            private set
            {
                if (isLogin!=value)
                {
                    isLogin = value;
                    IsLoginChanged?.Invoke(value);
                }
            }
        }

        public event Action<bool> IsLoginChanged;
        public event Action<bool> IsInitializedChanged;
        private bool isInitialized = false;
        public bool IsInitialized { 
            get 
            {
                return isInitialized;
            }
            private set 
            {
                if(isInitialized!=value)
                {
                    isInitialized = value;
                    IsInitializedChanged?.Invoke(value);
                }
            }
        }

        internal void RequestQrCodeUpdate()
        {
            onlineSDK.RequestQrCodeUpdate();
        }

        internal void FetchAlarmList()
        {
            onlineSDK.FetchAlarmList();
        }

        public async void FetchIOTDevices()
        {
            await onlineSDK.GetDeviceListAsync().ConfigureAwait(false);
        }

        public async void CheckNetworkState()
        {
            await onlineSDK.CheckNetworkState().ConfigureAwait(false);
        }

        public async Task ModifyAlarmAsync(int opt, JsonObject clockInfo)
        {
            await onlineSDK.ModifyAlarmAsync(opt, clockInfo).ConfigureAwait(false);
        }

        internal void StartKeywordRecognize()
        {
            onlineSDK.StartKeywordRecognize();
        }

        public string RequestTTS(string txt)
        {
            return onlineSDK.TextToVoice(txt);
        }
       

        #region Data
        public bool SaveSpeechEnabled
        {
            get
            {
                return onlineSDK.SaveSpeechEnabled;
            }
            set
            {
                onlineSDK.SaveSpeechEnabled = value;
            }
        }
        public bool SaveLogEnabled
        {
            get
            {
                return onlineSDK.SaveLogEnabled;
            }
            set
            {
                onlineSDK.SaveLogEnabled = value;
            }
        }
        public int LogLevel
        {
            get
            {
                return onlineSDK.LogLevel;
            }
            set
            {
                onlineSDK.LogLevel = value;
            }
        }
        public bool KeywordVerificationEnabled
        {
            get
            {
                return onlineSDK.KeywordVerificationEnabled;
            }
            set
            {
                onlineSDK.KeywordVerificationEnabled = value;
            }
        }

        public string UserId => onlineSDK.UserId;
        public string UserName => onlineSDK.UserName;

        public event Action<string> ResponseTextChanged;
        private string responseText = "";
        public string ResponseText
        {
            get
            {
                return responseText;
            }
            private set
            {
                if (responseText != value)
                {
                    responseText = value;
                    ResponseTextChanged?.Invoke(value);
                }
            }
        }

        #endregion
        /// <summary>
        /// Active the AI assistant,it will set state to ListeningPrepare.
        /// </summary>
        /// <param name="initResponse">The text show in the begin of active</param>
        /// <returns>Active successful or fail</returns>
        public bool Active(string initResponse = "")
        {
            if(!CanActive)
            {
                return false;
            }
            ResponseText = initResponse;
            XiaoweiAIState = XiaoweiAIStateEnum.ListeningPrepare;
            return true;
        }

        public bool CanActive
        {
            get
            {
                if (!IsLogin)
                {
                    Log.WriteLine("Can't active because not login");
                    return false;
                }
                if (XiaoweiAIState == XiaoweiAIStateEnum.ListeningPrepare || XiaoweiAIState == XiaoweiAIStateEnum.Listening || XiaoweiAIState == XiaoweiAIStateEnum.Thinking)
                {
                    Log.WriteLine("Can't active because already actived.");
                    return false;
                }
                if (XiaoweiAIState == XiaoweiAIStateEnum.Mute)
                {
                    Log.WriteLine("Can't active because it is in Mute state.");
                    return false;
                }
                return true;
            }
        }

        public void PrepareEnd()
        {
            if(XiaoweiAIState!= XiaoweiAIStateEnum.ListeningPrepare)
            {
                Log.WriteLine("Call PrepareEnd function should current state be ListeningPrepare.");
                return;
            }
            Log.WriteLine("Prepare end, now let's begin to listen.");
            XiaoweiAIState = XiaoweiAIStateEnum.Listening;
        }

        internal void GetResourceURL(string[] vs)
        {
            onlineSDK.GetResourceURL(vs);
        }

        private void StartSpeechRecognize()
        {
            if (IsOnlineMode)
            {
                Debug.WriteLine("StartSpeechRecognize online");
                onlineSDK.StartSpeechRecognize();
            }
            else
            {
                Debug.WriteLine("StartSpeechRecognize offline");
                offlineSDK.StartSpeechRecognize();
            }

        }

        internal void GetMoreResources(Skill skill, string playID, bool v)
        {
            onlineSDK.GetMoreResources(skill, playID, v);
        }

        private void StopSpeechRecognize()
        {
            if (IsOnlineMode)
            {
                Debug.WriteLine("StopSpeechRecognize online");
                onlineSDK.StopSpeechRecognize();
            }
            else
            {
                Debug.WriteLine("StopSpeechRecognize offline");
                offlineSDK.StopSpeechRecognize();
            }


            // test offline 
            //int ret = XiaoWeiOffLineSDK.Instance.offLine_RockhopperFSStopEngine();
            //Log.WriteLine("SendSpeechAudio ret is=" + ret);

        }

        public async Task SendSpeechAudio(IPcmData pcm)
        {
            if (IsMute)
            {
                Debug.WriteLine("Because of mute,send abort");
                return;
            }
            if (IsOnlineMode)
            {
                Debug.WriteLine("SendSpeech online");
                await onlineSDK.SendSpeechAudio(pcm.getData()).ConfigureAwait(false);
            }
            else
            {
                Debug.WriteLine("SendSpeech offline");
                await offlineSDK.SendSpeechAudio(pcm.getData()).ConfigureAwait(false);
            }
        }

        public async Task AbortRequestAsync()
        {
            if (!IsMute)
            {
                XiaoweiAIState = XiaoweiAIStateEnum.Idle;
            }
            if (IsOnlineMode)
            {
                await onlineSDK.AbortRequestAsync().ConfigureAwait(false);
            }
            else
            {
                await offlineSDK.AbortRequestAsync().ConfigureAwait(false);
            }
        }

        /// <summary>
        /// because can't play tts in this service,we must tell it the tts has play begin or end.
        /// </summary>
        /// <param name="state">if true,tts play is begining,false indicate the tts is end.</param>
        /// <returns>common return true,just can't turn state to speaking will false.</returns>
        public bool SetSpeakingState(bool state)
        {
            if(state)
            {
                if(XiaoweiAIState== XiaoweiAIStateEnum.Mute)
                {
                    Log.WriteLine("Can't speak tts because current state is mute.");
                    return false;
                }
                if (XiaoweiAIState == XiaoweiAIStateEnum.Listening)
                {
                    Log.WriteLine("Can't speak tts because current state is Listening,play tts should abort active.");
                    return false;
                }
                //if (XiaoweiAIState == XiaoweiAIStateEnum.Speaking)
                //{
                //    Log.WriteLine("Can't speak tts because current state is Speaking,play tts should end current tts.");
                //    return false;
                //}
                if (XiaoweiAIState == XiaoweiAIStateEnum.ListeningPrepare)
                {
                    Log.WriteLine("Can't speak tts because current state is ListeningPrepare.");
                    return false;
                }
                XiaoweiAIState = XiaoweiAIStateEnum.Speaking;
                return true;
            }
            else
            {
                if (XiaoweiAIState == XiaoweiAIStateEnum.Speaking)
                {
                    if(HasContext)
                    {
                        Active();
                    }
                    else
                    {
                        XiaoweiAIState = XiaoweiAIStateEnum.Idle;
                    }

                }
                return true;
            }
        }

        internal void SendKeywordAudio(byte[] pcm)
        {
            onlineSDK.SendKeywordAudio(pcm);
        }

        internal void ClearHistory()
        {
            voiceAIManager.ClearHistory();
        }

        public bool IsMute => XiaoweiAIState == XiaoweiAIStateEnum.Mute;
        /// <summary>
        /// Set the state to Mute or unmute.
        /// </summary>
        /// <param name="isMute">True is mute or false is unmute.</param>
        public async void SetMute(bool isMute)
        {
            if (IsMute == isMute)
            {
                return;
            }
            if (isMute)
            {
                await AbortRequestAsync().ConfigureAwait(false);
                XiaoweiAIState = XiaoweiAIStateEnum.Mute;
            }
            else
            {
                XiaoweiAIState = XiaoweiAIStateEnum.Idle;
            }
        }
        public void UpdateAppList(IList<string> appList)
        {
            onlineSDK.UpdateAppList(appList);
        }

        public void Logout()
        {
            onlineSDK.EraseBinders();
        }

        public event NotifyCollectionChangedEventHandler HistoryChanged;
        private void History_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            HistoryChanged?.Invoke(sender, e);
        }

        public IReadOnlyList<IResponseData> History
        {
            get
            {
                return voiceAIManager.History;
            }
        }

        private IntelLocalVoiceAI.IntelLocalVoiceAI offlineSDK;
        private XiaoweiVoiceAI.XiaoweiVoiceAI onlineSDK;
        private VoiceAIManager voiceAIManager;
        private CustomVoiceAI customVoiceAI;
        private static readonly Lazy<XiaoweiAI> LazyInstance = new Lazy<XiaoweiAI>(() =>
        {
            return new XiaoweiAI();
        }, LazyThreadSafetyMode.ExecutionAndPublication);
        private XiaoweiAI()
        {
            VoiceAI.Diagnostics.Debug.LogReceived += LogReceived;
            XiaoweiVoiceAI.Diagnostics.Debug.LogReceived += LogReceived;
            IntelLocalVoiceAI.Diagnostics.Debug.LogReceived += LogReceived;
            voiceAIManager = new VoiceAI.VoiceAIManager();
            // onlineSDK = new XiaoweiVoiceAI.XiaoweiVoiceAI();
            onlineSDK = XiaoweiVoiceAI.XiaoweiVoiceAI.Instance;
            offlineSDK = new IntelLocalVoiceAI.IntelLocalVoiceAI();
            //offlineSDK.SetDefaultTextAndUrl(CommonReplyModel.NoRecongizeCommand, CommonReplyModel.OffLineModeTooltip, Audios.OffLineModeTooltip);
            customVoiceAI = new CustomVoiceAI();
            ResponseHandlerRegister.RegisterResponseHandlers(voiceAIManager);
            voiceAIManager.RegisterVoiceAI(onlineSDK);
            voiceAIManager.RegisterVoiceAI(offlineSDK);
            voiceAIManager.RegisterVoiceAI(customVoiceAI);
            voiceAIManager.HistoryChanged += History_CollectionChanged;
            voiceAIManager.ResponseReceived += (response) => { ResponseReceived?.Invoke(response); };
            onlineSDK.Login += (id, name) =>
            {
                if (FirstTimeUsed)
                {
                    Logout();
                }
                else
                {
                    IsLogin = true;
                }

            };
            onlineSDK.Logout += () => IsLogin = false;
            onlineSDK.Initialized += () =>
            {
                FirstTimeUsed = false;
                IsInitialized = true;
            };
            //kws
            onlineSDK.KeywordConfirmed += () => KeywordConfirmed?.Invoke();
            onlineSDK.KeywordRejected += () => KeywordRejected?.Invoke();
            onlineSDK.SpeechRecognizing += (t) => { ResponseText = t; };
            onlineSDK.SpeechRecognized += (t) =>
            {
                XiaoweiAIState = XiaoweiAIStateEnum.Thinking;
            };

            offlineSDK.SpeechRecognizing += (t) => { ResponseText = t; };
            offlineSDK.SpeechRecognizeEnded += () =>
            {
                XiaoweiAIState = XiaoweiAIStateEnum.Thinking;
            };

            //XiaoweiSDK.Instance.ProcessEnded += () => ProcessEnded?.Invoke();

            onlineSDK.AvailabilityChanged += (online) => {
                OnlineStateChanged?.Invoke(online);
                IsOnlineMode = Connectivity.IsAvailable && online;
            };
            onlineSDK.ResponseError += (err) =>
            {
                //fix 6730
                if (IsOnlineMode)
                {
                    XiaoweiAIState = XiaoweiAIStateEnum.Idle;
                }
                ResponseErrored?.Invoke(err);
            };


            onlineSDK.QrCodeChanged += (path) => QrCodeChanged?.Invoke(path);
            //XiaoweiSDK.Instance.AlarmChanged += (json) => AlarmChanged?.Invoke(json);
            onlineSDK.CountChanged += (count) => CountChanged?.Invoke(count);
            Connectivity.AvailabilityChanged += (state) => {
                IsOnlineMode = state && IsOnline;
            };
        }

        private void LogReceived(string log)
        {
            Debug.WriteLine(log);
        }
        public event Action<IResponseData> ResponseReceived;
        public static XiaoweiAI Instance { get { return LazyInstance.Value; } }
        private bool FirstTimeUsed { get; set; } = true;

        private bool inited = false;

        public bool HasContext => IsOnlineMode && onlineSDK.HasContext;

        public bool HasPlayable
        {
            get
            {
              return  onlineSDK.HasPlayable;
            }
        }

        public void BeatHeart()
        {
            onlineSDK.BeatHeart();
        }
        public void SendCustomResponse(IResponseData responseData)
        {
            customVoiceAI.SendCustomResponse(responseData);
        }
        public void Init(string sn, string signature, string appId, int pid, int keyVersion,bool firstTimeUsed)
        {
            if(inited)
            {
                return;
            }
            inited = false;
            FirstTimeUsed = firstTimeUsed;

            if (!string.IsNullOrEmpty(sn) && !string.IsNullOrEmpty(signature) && !string.IsNullOrEmpty(appId))
            {
                onlineSDK.InitAsync(sn, signature, appId, pid, keyVersion);
            }
            _ = offlineSDK.InitializeAsync();

        }

    }
}
